package au.edu.unsw.editingtool.transformxml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.UUID;

import org.apache.commons.lang.StringEscapeUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import fedora.server.types.gen.Datastream;

import au.edu.unsw.editingtool.fedora.FedoraFactory;
import au.edu.unsw.editingtool.global.Util;

public class ValetXmlCreator {

    private org.dom4j.Document valetDocument;
    private Element root;
    private FileWriter out;

    private Element formData;
    private Element attachments;
    
    public ValetXmlCreator() {    
    	this.valetDocument = DocumentHelper.createDocument();
    }

    public void load(File xmlFile) throws DocumentException {
        SAXReader reader = new SAXReader();
        valetDocument = reader.read(xmlFile);
        this.root = valetDocument.getRootElement();
        this.formData = (Element) valetDocument.selectSingleNode("Formdata");

    }

    public Document getDocument() {
        return valetDocument;
    }

    private String escapeHtml(String s) {
        String htmlEscaped = StringEscapeUtils.escapeHtml(s);
        return htmlEscaped;
    }

    /**
     * This method creates a new document object (Valet XML Document Object)
     * 
     * @param nodeName (String) nodeName is the name you would like to assign to
     * the root node of the Valet XML Document Object.
     */
    public void createValetDocument(String nodeName) {        
        root = this.valetDocument.addElement(nodeName);
    }

    /**
     * This method creates a Formdata element to the root node of the Valet XML
     * Document Object
     */
    public void createFormDataElement() {
    	Boolean formdataexist = false;
    	try{
    		if (this.valetDocument.selectSingleNode("//Formdata") != null) {
    			formdataexist = true;
    		}		
    	}
        catch(Exception ex)
        {
        	formdataexist = false;
        }
        if(!formdataexist);
        {
        	formData = this.root.addElement("Formdata");
        }          
    }

    /* The attachment info on VALETXML will not be used since the attachment
     * will be ingested and set inactive after the upload.
     */
    /**
     * This method creates an Attachments element to the root node of the Valet
     * XML Document Object
     */
    public void createAttachmentsElement() {
        attachments = this.root.addElement("Attachments");
    }

    /**
     * This method adds an attachment element to the Valet XML and adds the
     * original filename and new given filename (attribute) Document Object. If
     * no Attachments (plural) element exists then this method will create it
     */
    public void addSingleAttachment(String filename, String attribute) {
        if (valetDocument.selectSingleNode("//Attachments") == null) {
            this.createAttachmentsElement();
        }
        Element singleAttachmentElement = this.attachments.addElement("Attachment");
        singleAttachmentElement.addAttribute("id", attribute);
        Element elementObject2 = singleAttachmentElement.addElement("original_filename");
        elementObject2.addText(this.escapeHtml(filename));

    }

    public void addSingleAttachment(String filename, String attribute,
        Hashtable<String, String> elementData) {
        if (valetDocument.selectSingleNode("//Attachments") == null) {
            this.createAttachmentsElement();
        }
        Element singleAttachmentElement = this.attachments.addElement("Attachment");
        singleAttachmentElement.addAttribute("id", attribute);
        Element elementObject2 = singleAttachmentElement.addElement("original_filename");
        elementObject2.addText(this.escapeHtml(filename));

        // Add extra data from form
        for (Enumeration e = elementData.keys(); e.hasMoreElements();) {
            String elementName = (String) e.nextElement();
            String elementContents = (String) elementData.get(elementName);
            Element elementObject = singleAttachmentElement.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method adds new elements to the root node of the VALET XML Document
     * Object
     * 
     * @param elementHashtable The method accepts a Hashtable where the Key is
     * the Element name and the Value is the contents of the newly created
     * Element root key value key value key value key etc...
     * 
     */
    public void createChildElementsWithContentsUsingHashtable(
        Hashtable<String, String> elementHashtable) {
        for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
            String elementName = (String) e.nextElement();
            String elementContents = (String) elementHashtable.get(elementName);
            Element elementObject = root.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method adds new elements NOT to the root of the VALET XML Document
     * Object but to a new Element that it creates using the "parentElementName"
     * parameter
     * 
     * @param elementHashtable The method accepts a Hashtable where the Key is
     * the Element name and the Value is the contents of the newly created
     * Element
     * @param parentElementName If you provide the String "formdata" or
     * "Formdata" as the parentElementName the Hashtables contents will be added
     * inside the Formdata element. If the Formdata element does not exist it
     * will be created.
     */
    public void createChildElementsWithContentsUsingHashtable(
        Hashtable<String, String> elementHashtable, String parentElementName) {
        if (parentElementName.equalsIgnoreCase("formdata")) {
            this.createFormDataElement();
            for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
                String elementName = (String) e.nextElement();
                String elementContents = (String) elementHashtable.get(elementName);
                Element elementObject = formData.addElement(elementName);
                elementObject.addText(this.escapeHtml(elementContents));
            }

        } else {
            Element newElement;
            newElement = root.addElement(parentElementName);
            for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
                String elementName = (String) e.nextElement();
                String elementContents = (String) elementHashtable.get(elementName);
                Element elementObject = newElement.addElement(elementName);
                elementObject.addText(this.escapeHtml(elementContents));
            }
        }
    }

    /**
     * This method adds a new Element to the root node of the VALET XML Document
     * Object
     * 
     * @param elementName The method accepts a String name for the Element
     * @param elementContents The method accepts a String for the contents
     * <root><elementName>elementContents</elementName> etc...
     */
    public void createChildElementWithContentUsingStrings(String elementName,
        String elementContents) {
        Element elementObject = root.addElement(elementName);
        elementObject.addText(this.escapeHtml(elementContents));
    }

    /**
     * This method adds a new Element NOT to the root node of the VALET XML
     * Document Object but to a new Element that is creates using the parameter
     * parentElementName
     * 
     * @param elementName The method accepts a String name for the Element
     * @param elementContents The method accepts a String for the contents
     * @param parentElementName This method accepts a String for the new Element
     * to be created <root><parentElementName><elementName>elementContents</
     * elementName></parentElementName>
     */
    public void createChildElementWithContentUsingStrings(String elementName,
        String elementContents, String parentElementName) {
        if (parentElementName.equalsIgnoreCase("formdata")) {
            this.createFormDataElement();
            Element elementObject = formData.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        } else {
            Element newElement;
            newElement = root.addElement(parentElementName);
            Element elementObject = newElement.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method returns a unique session ID for the VALET object
     * 
     */
    public String createUniqueId() {
        return UUID.randomUUID().toString();
    }

    // Save valetxml to temporary folder.
    public void saveValetXmlDocument(String directoryName, String prefix, String suffix) throws IOException {
    	if (!new File(directoryName).exists()) {
    		if (!new File(directoryName).mkdir()) 
    			directoryName = System.getProperty("java.io.tmpdir");
    	}
    	
    	//Create the temporary file
    	String file = Util.createTempFile(prefix, suffix, directoryName);
    	
    	// Print data to temporary file
    	out = new FileWriter(file);
        this.valetDocument.write(out);
        out.close();
    }
    
    // 
    public void serializeValetXmlDocument(String uuid, String directoryName,
        String valetPid) {

        try {
            String outputString = directoryName + "/" + valetPid + "-" + uuid;
            out = new FileWriter(outputString);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            this.valetDocument.write(out);
            // System.out.println("Writing Zip file to local disk");
            System.out.println(directoryName + "/" + uuid);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    
    // Update the metadata using Fedora class
    // 1. Generate the new metadata by transforming the valet xml using the xsl supplied.
    // 2. The transformation will use VALET Perl XSL file.
    // 3. Using Fedora class to update the datastream metadata which is indicated on the ds id.
    public String fedoraUpdateMetadata(String fedoraAddress, String username, String password,
    						 String pid, String datastreamId, String label, String xsl) {
    	
    		String date = null;
        try {
            FedoraFactory fedora = new FedoraFactory(fedoraAddress, username, password);            
            byte[] metadata = fedora.transformXML(valetDocument.asXML().getBytes("UTF-8"), xsl);
            
            //String metadataString = new String(metadata);
            //System.out.println("Metadata = "+metadata);
            if (metadata == null)
            {
            	return date;
            }
            //System.out.println("[VALETXMLCREATOR] fedoraUpdateMetadata valetDocument.asXML().toString());
            //System.out.println(new String(metadata));
            
            Datastream dsInfo = null;
            try{
            	
    			dsInfo = fedora.getDatastreamInfo(pid,datastreamId);    			
    			/* DEBUG  
    		    System.out.println("PID="+pid+"|dsID="+datastreamId);
    			System.out.println("[VALETXMLCREATOR] fedoraUpdateMetadata dsInfo START ================");
    			System.out.println("DS ID: "+dsInfo.getID());    			
    			System.out.println("DS AltIDs: "+dsInfo.getAltIDs().toString());
    			System.out.println("DS LABEL: "+dsInfo.getLabel());
    			System.out.println("DS MimeType: "+dsInfo.getMIMEType());
    			System.out.println("DS FormatURI: "+dsInfo.getFormatURI());
    			System.out.println("DS Location: "+dsInfo.getLocation());
    			System.out.println("DS CheckSum: "+dsInfo.getChecksum());
    			System.out.println("DS CheckSum Type: "+dsInfo.getChecksumType());
    			System.out.println("DS CreateDate: "+dsInfo.getCreateDate());
    			System.out.println("DS Size: "+dsInfo.getSize());
    			System.out.println("DS State: "+dsInfo.getState());
    			System.out.println("DS VersionID: "+dsInfo.getVersionID());
    			System.out.println("DS ControlGroup: "+dsInfo.getControlGroup());
    			System.out.println("DS isVersionable: "+dsInfo.isVersionable());
    			System.out.println("[VALETXMLCREATOR] fedoraUpdateMetadata dsInfo END ==================");
    			 */ 			    			    			    		    		
    		}
    		catch(Exception e)
    		{
    			//EditingToolException ete = new EditingToolException();
               // ete.setMessage("Failed getting the datastream info for modifying Datastream by value for PID: " + 
                				//pidV + 
                				//" and datastream ID: " + datastreamID);
                //ete.printStackTrace();                
                //System.out.println(ete.sendMessage());
                //throw ete;
                e.printStackTrace();			
    		}
            date = fedora.modifyDatastreamByValue(pid, datastreamId, dsInfo.getAltIDs(),
            		dsInfo.getLabel(), dsInfo.getMIMEType(), dsInfo.getFormatURI() ,metadata ,
                    null, null, "Metadata update", false);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return date;
    }
    
    /*

    public void fedoraUpdate(String pid, String datstreamId, String dcpath) {
        try {
            FedoraFactory fedora = new FedoraFactory(
                "http://localhost:8080/fedora", "fedoraAdmin", "fedoraAdmin");
            byte[] dc = fedora.transformXML(valetDocument.asXML().getBytes(
                "UTF-8"), dcpath);
            // modify dc
            fedora.modifyDatastreamByValue(pid, "DC", new String[] {},
                "Changed Stage", "text/xml", dc, "A", "prashant");
            // modify valetxml
            fedora.modifyDatastreamByValue(pid, "VALETXML", new String[] {},
                "Changed Stage", "text/xml", valetDocument.asXML().getBytes(
                    "UTF-8"), "A", "prashant");
            // fedora.modifyDatastreamByValue(pid, datastreamID, new Str, label,
            // contentType, content, state, log)
            String label = fedora.getLabel(pid);
            if (label != null) {
                String newLabel = "";
                String state = "D";
                if ("valet stage1".equals(label)) {
                    newLabel = "valet stage2";
                } else if ("valet stage2".equals(label)) {
                    newLabel = "valet published";
                    state = "A";
                }
                fedora.setLabel(pid, newLabel, state);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String fedoraCreate(String datstreamId, String dcpath) {
        try {
        	FedoraFactory fedora = new FedoraFactory(
                "http://localhost:8080/fedora", "fedoraAdmin", "fedoraAdmin");
            byte[] dc = fedora.transformXML(valetDocument.asXML().getBytes(
                "UTF-8"), dcpath);
            byte[] foxml = fedora.writeFoxml(datstreamId, "valet xml",
                valetDocument.asXML(), new String(dc), "valet stage1", "I");
            System.out.println("foxml=" + new String(foxml));
            String pid = fedora.ingestObject(foxml);
            System.out.println("new pid=" + pid);
            return pid;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
    */
}
